package com.virjar.vscrawler.core;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.virjar.vscrawler.core.config.VSCrawlerConfigFileWatcher;
import com.virjar.vscrawler.core.event.EventLoop;
import com.virjar.vscrawler.core.event.support.AutoEventRegistry;
import com.virjar.vscrawler.core.resourcemanager.ResourceManager;
import com.virjar.vscrawler.core.resourcemanager.model.ResourceSetting;
import com.virjar.vscrawler.core.resourcemanager.service.QueueStore;
import com.virjar.vscrawler.core.util.PathResolver;
import com.virjar.vscrawler.core.util.VSCrawlerConstant;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.util.Collection;
import java.util.Properties;
import java.util.concurrent.ConcurrentMap;

/**
 * Created by virjar on 17/5/4.<br/>
 * context,存放上下文信息,多组件数据共享。这个在0.2.x上面实现,他的实现代表多站点爬虫开始实现
 *
 * @author virjar
 * @since 0.2.0
 */
@Slf4j
@RequiredArgsConstructor
public class VSCrawlerContext {


    @Getter
    @NonNull
    // 爬虫名称
    private String crawlerName;


    @Getter
    // 工作路径
    private String workPath;


    @Getter
    @NonNull
    // 事件循环
    private EventLoop eventLoop;

    @Getter
    @Setter
    private AutoEventRegistry autoEventRegistry;

    @Getter
    @Setter
    private ResourceManager resourceManager;

    @Getter
    @Setter
    private QueueStore queueStore;

    @Getter
    @Setter
    private ResourceSetting resourceSetting;

    // 爬虫主控文件监听器
    public static VSCrawlerConfigFileWatcher vsCrawlerConfigFileWatcher = new VSCrawlerConfigFileWatcher();

    private static ConcurrentMap<String, VSCrawlerContext> allContext = Maps.newConcurrentMap();

    public static Collection<VSCrawlerContext> getAllContext() {
        return Lists.newArrayList(allContext.values());
    }

    public static void removeContext(VSCrawlerContext vsCrawlerContext) {
        allContext.remove(vsCrawlerContext.getCrawlerName());
    }

    private void resolveWorkPath() {
        Properties properties = vsCrawlerConfigFileWatcher.loadedProperties();
        workPath = properties.getProperty(VSCrawlerConstant.VSCRAWLER_WORKING_DIRECTORY, "file:~/.vscrawler/work");

        log.info("vsCrawler配置工作目录:{}", workPath);
        workPath = PathResolver.resolveAbsolutePath(workPath);
        workPath = new File(workPath, crawlerName).getAbsolutePath();
        log.info("vsCrawler实际工作目录:{}", workPath);
        File workFile = new File(workPath);
        if (workFile.exists() && !workFile.isDirectory()) {
            throw new IllegalStateException(workPath + "不是目录,无法写入数据");
        }

        if (!workFile.exists()) {
            if (!workFile.mkdirs()) {
                throw new IllegalStateException(workPath + "文件夹创建失败");
            }
        }
    }

    /**
     * 1. 建立一个新的爬虫Instance
     * 2. 一个map存放许多crawlerName对应的instance
     *     1. 首先检测map里面是否含有crawlerName
     *     2. 有则直接返回
     *     3. 木有则创建新的
     * 3. 创建新的时候，加锁：防止多线程创建相同的
     *     1. 加锁之后再次检测map里是否含有crawlerName
     *     2. 注意VSCrawlerContext里面包含属性： crawlerName(名称)、workpath、eventLoop、autoEventRegister、resourceManager、queueStore、 resourceStore
     *     3. 首先设置crawlerName、eventLoop? 为什么呢？
     *     4. 因为你要设置autoEventRegister对象，创建autoEventRegister需要传递vscrawlercontext,而autoEventRegister属性里面使用到了eventLoop
     *     5. 解决路径问题
     *     6. 解决注册观察
     *
     * @param crawlerName
     * @return
     */
    public static VSCrawlerContext create(String crawlerName) {
        if (allContext.containsKey(crawlerName)) {
            return allContext.get(crawlerName);
        }
        synchronized (VSCrawlerContext.class) {
            if (allContext.containsKey(crawlerName)) {
                return allContext.get(crawlerName);
            }

            VSCrawlerContext vsCrawlerContext = new VSCrawlerContext(crawlerName, new EventLoop());
            AutoEventRegistry autoEventRegistry = new AutoEventRegistry(vsCrawlerContext);
            vsCrawlerContext.setAutoEventRegistry(autoEventRegistry);
            vsCrawlerContext.resolveWorkPath();
            vsCrawlerContext.getAutoEventRegistry().registerObserver(vsCrawlerConfigFileWatcher);
            allContext.put(crawlerName, vsCrawlerContext);
            return vsCrawlerContext;
        }
    }

    public String makeUserResourceTag() {
        return getCrawlerName() + "_userManagerAccountKey";
    }

}
